home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
EDITOR
/
DTE5_1.ARJ
/
ED.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-02-06
|
45KB
|
1,459 lines
/*
* Written by Douglas Thomson (1989/1990)
*
* This source code is released into the public domain.
*/
/*
* Name: dte - Doug's Text Editor program - main editor module
* Purpose: This file contains the main editor module, and a number of the
* smaller miscellaneous editing commands.
* It also contains the code for dispatching commands.
* File: ed.c
* Author: Douglas Thomson
* System: this file is intended to be system-independent
* Date: October 1, 1989
* I/O: file being edited
* files read or written
* user commands and prompts
* Notes: see the file "dte.doc" for general program documentation
*/
#ifdef HPXL
#include "commonh" /* common types for all dte modules */
#include "globalh" /* global variables */
#include "findreph" /* find and replace command prototypes */
#include "blockh" /* block marking, moving etc prototypes */
#include "windowh" /* opening and sizing windows, help */
#include "utilsh" /* miscellaneous commonly used routines */
#else
#include "common.h" /* common types for all dte modules */
#include "global.h" /* global variables */
#include "findrep.h" /* find and replace command prototypes */
#include "block.h" /* block marking, moving etc prototypes */
#include "window.h" /* opening and sizing windows, help */
#include "utils.h" /* miscellaneous commonly used routines */
#endif
#include <time.h> /* for auto-saving */
/*
* prototypes for all functions in this file
*/
void quit ARGS((windows *window));
void tab_key ARGS((windows *window));
void insert ARGS((windows *window, int c, int new_line));
void move_up ARGS((windows *window));
void move_down ARGS((windows *window));
void move_left ARGS((windows *window));
void move_right ARGS((windows *window));
void word_left ARGS((windows *window));
void word_right ARGS((windows *window));
void word_delete ARGS((windows *window));
void char_del_left ARGS((windows *window));
void line_kill ARGS((windows *window));
void char_del_under ARGS((windows *window));
void eol_kill ARGS((windows *window));
void reminder ARGS((char *mess));
void goto_left ARGS((windows *window));
void goto_right ARGS((windows *window));
void goto_top ARGS((windows *window));
void goto_bottom ARGS((windows *window));
void set_tabstop ARGS((void));
int command ARGS((windows *window));
void editor ARGS((int argc, char *argv[]));
/*
* Name: quit
* Purpose: To close the current window without saving the current file.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the file has been modified but not saved, then the user is
* given a second chance before the changes are discarded.
* Note that this is only necessary if this is the last window
* that refers to the file. If another window still refers to
* the file, then the check can be left until later.
*/
void quit(window)
windows *window;
{
if (window->file_info->modified && window->file_info->ref_count == 1) {
set_prompt("Abandon changes? (y/n): ", 1);
if (display(get_yn, 1) != A_YES) {
return;
}
}
/*
* If the user decided to abandon changes, then the recovery file (if
* one exists) is now obsolete.
*/
if (g_status.recovery[0]) {
hw_unlink(g_status.recovery);
g_status.recovery[0] = '\0';
}
/*
* remove window, allocate screen lines to other windows etc
*/
finish(window);
}
/*
* Name: tab_key
* Purpose: To make the necessary changes after the user types the tab key.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If in insert mode, then this function simply puts the required
* number of spaces back into the input stream, so as far as the
* editor is concerned the tab is ignored, and the user then typed
* the spaces instead.
* If not in insert mode, then tab simply moves the cursor right
* the required distance.
*/
void tab_key(window)
windows *window;
{
int spaces; /* the spaces to move to the next tab stop */
/*
* work out the number of spaces to the next tab stop
*/
spaces = g_status.tab_size - (window->ccol % g_status.tab_size);
if (g_status.insert) {
/*
* pretend the user actually typed the spaces. All the work will
* be done by the insert function.
*/
while (spaces--) {
c_uninput(' ');
}
}
else {
/*
* advance the cursor without changing the text underneath
*/
window->ccol += spaces;
/*
* make sure the cursor stays on the screen
*/
if (window->ccol >= g_display.ncols) {
window->ccol = g_display.ncols-1;
}
}
}
/*
* Name: insert
* Purpose: To make the necessary changes after the user has typed a normal
* printable character (or a carriage return)
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* c: the character just typed
* new_line: TRUE if carriage return, FALSE if insert line
*/
void insert(window, c, new_line)
windows *window;
int c;
int new_line;
{
text_ptr source; /* source for block move to make room for c */
text_ptr dest; /* destination for block move */
long number; /* number of characters to be moved */
int len; /* length of current line */
int pad; /* padding to add if cursor beyond end of line */
int add; /* characters to be added (usually 1 in insert mode) */
int cr; /* does current line end in \n? */
int i; /* counter for adding autoindenting */
text_ptr prev; /* previous lines scanned for autoindent */
/*
* first check we have room on the screen - although the editor can
* cope with lines wider than the screen, we do not want to
* encourage them!
*/
if (window->ccol >= g_display.ncols-1 && c != '\n') {
error(WARNING, "cannot insert more characters");
return;
}
/*
* if necessary, copy the current line into the line buffer. Making
* small changes to the entire file is too slow, so only the current
* line is affected until the cursor moves to another line.
*/
copy_line(window);
/*
* work out how many characters need to be inserted
*/
len = linelen(g_status.line_buff);
if (g_status.line_buff[len] == '\n') {
cr = 1;
}
else {
cr = 0;
}
if (window->ccol > len) { /* padding required */
pad = window->ccol - len;
}
else {
pad = 0;
}
if (c == '\n') {
add = 0;
/*
* indentation is only required if we are in the right mode,
* the user typed <CR>, and if there is not space followed
* by something after the cursor.
*/
if (g_status.indent && new_line && (window->ccol >= len ||
g_status.line_buff[window->ccol] != ' ')) {
/*
* autoindentation is required. Match the indentation of
* the first line above that is not blank.
*/
add = first_non_blank(g_status.line_buff);
if (g_status.line_buff[add] == '\n' ||
g_status.line_buff[add] == '\0') {
prev = window->cursor;
while ((prev = find_prev(prev)) != NULL) {
add = first_non_blank((char *)prev);
if (prev[add] != '\n') {
break;
}
}
}
}
++add; /* carriage return is always inserted, even in overwrite */
}
else if (g_status.insert || window->ccol >= len) {
/*
* inserted characters, or overwritten characters at the end of
* the line, are inserted.
*/
add = 1;
}
else {
/*
* If the character is not a carriage return, and the cursor is
* in the middle of the line, and we are not in insert mode,
* then the current character is overwritten by the new one,
* and no extra space is required.
*/
add = 0;
}
/*
* check that current line would not get too long. Note that there must
* be space for both the old line and any indentation, so the maximum
* allowed line length (BUFF_SIZE) should be at least twice the
* actual screen line length.
*/
if (len + pad + add + cr >= BUFF_SIZE) {
error(WARNING, "no more room to add");
return;
}
/*
* all clear to add new character!
*/
/*
* move character to make room for whatever needs to be inserted
*/
source = g_status.line_buff + window->ccol - pad;
dest = source + pad + add;
number = len + pad - window->ccol + 1 + cr;
hw_move(dest, source, number);
/*
* fix marks (such as block begin/end) so that they remain in the
* correct place after the move
*/
fix_marks(window, source, (long) (pad + add));
/*
* if padding was required, then put in the required spaces
*/
while (pad--) {
*source++ = ' ';
}
/*
* now place the new character (which was included in the "add" count)
*/
*source++ = c;
--add;
/*
* now put in the autoindent characters
*/
for (i=0; i < add; i++) {
*source++ = ' ';
}
if (c == '\n') {
/*
* the line has been split. This is a special case, since the
* current line now has (or may have) a carriage return in the
* middle of it, which is not normally allowed. Hence we must
* restore the situation to a safe state.
*/
un_copy_line(window);
/*
* make sure the line below the cursor is visible
*/
if (window->cline == window->bottom_line) {
if (window->cline > window->top_line) {
scroll_down(window);
}
}
/*
* give the display routine a hint about a way of updating the
* screen that is likely to be efficient.
*/
if (window->cline < window->bottom_line) {
window_scroll_down(window->cline+1, window->bottom_line);
}
/*
* If the cursor is to move down to the next line, then update
* the line and column appropriately.
*/
if (new_line) {
window->ccol = add;
window->cursor = find_next(window->cursor);
if (window->cline < window->bottom_line) {
window->cline++;
}
}
}
else {
/*
* This is a normal character in a normal part of the screen.
* Simply advance the cursor and output the character.
*/
xygoto(window->ccol, window->cline);
if (g_status.insert && window->ccol < len) {
c_insert();
}
c_output(c);
window->ccol++;
}
/*
* record that file has been modified (this is only necessary here
* if in overwrite mode [where pad and add can both be 0])
*/
window->file_info->modified = TRUE;
if (!g_status.unsaved) {
g_status.save_time = time(NULL);
g_status.unsaved = TRUE;
}
}
/*
* Name: move_up
* Purpose: To move the cursor one line up the screen.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is at the top of the window, then the file must
* be scrolled down.
* If the cursor is already on the first line of the file, then
* this command can be ignored.
*/
void move_up(window)
windows *window;
{
text_ptr p; /* the previous line on the screen */
un_copy_line(window);
/*
* if no previous line, give up
*/
if ((p = find_prev(window->cursor)) == NULL) {
return;
}
if (window->cline == window->top_line) {
window_scroll_down(window->top_line, window->bottom_line);
}
else {
--window->cline; /* simply move cursor */
}
window->cursor = p;
}
/*
* Name: move_down
* Purpose: To move the cursor one line down the screen.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is at the bottom of the window, then the file must
* be scrolled up.
* If the cursor is already on the last line of the file, then
* this command can be ignored.
*/
void move_down(window)
windows *window;
{
text_ptr p;
un_copy_line(window);
if ((p = find_next(window->cursor)) == NULL) {
return;
}
if (window->cline == window->bottom_line) {
window_scroll_up(window->top_line, window->bottom_line);
}
else {
++window->cline;
}
window->cursor = p;
}
/*
* Name: move_left
* Purpose: To move the cursor one character to the left
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is already at the left of the screen, then
* this command is ignored.
*/
void move_left(window)
windows *window;
{
if (window->ccol > 0) {
--window->ccol;
}
}
/*
* Name: move_right
* Purpose: To move the cursor one character to the right
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is already at the right of the screen, then
* this command is ignored.
* It is quite OK to move the cursor beyond the rightmost
* character in the line itself.
*/
void move_right(window)
windows *window;
{
if (window->ccol < g_display.ncols-1) {
++window->ccol;
}
}
/*
* Name: word_left
* Purpose: To move the cursor one word to the left.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is at the left of the line, then move the
* end of the previous line.
* If the cursor is beyond the end of the line, then move back
* to the end of the line.
* Words are considered strings of letters, numbers and underscores,
* which must be separated by other characters.
*/
void word_left(window)
windows *window;
{
text_ptr p; /* previous line in file */
int len; /* length of current line */
/*
* get current line in buffer so we can play with it
*/
copy_line(window);
if (window->ccol > (len = linelen(g_status.line_buff))) {
/*
* cursor beyond end of line
*/
window->ccol = len;
}
else if (window->ccol == 0) {
/*
* cursor at start of line
*/
un_copy_line(window);
if ((p = find_prev(window->cursor)) != NULL) {
if (window->cline == window->top_line) {
scroll_up(window);
}
window->cursor = p;
--window->cline;
window->ccol = linelen(window->cursor);
}
}
else {
/*
* normal search for word. Do not consider character under
* cursor, so that two word left commands in a row will keep
* moving the cursor.
*/
--window->ccol;
/*
* scan for something that IS part of a word
*/
for (;;) {
if (myisalnum(g_status.line_buff[window->ccol])) {
break;
}
if (window->ccol == 0) {
break;
}
--window->ccol;
}
/*
* now scan for something that is NOT part of a word
*/
if (window->ccol > 0) {
for (;;) {
if (!myisalnum(g_status.line_buff[window->ccol])) {
/*
* we have found something that is NOT part of a word,
* so the thing after it must be the start of a word.
*/
++window->ccol;
break;
}
if (window->ccol == 0) {
break;
}
--window->ccol;
}
}
}
}
/*
* Name: word_right
* Purpose: To move the cursor one word to the right.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is at the right of the line, then move the
* start of the next line.
* Words are considered strings of letters, numbers and underscores,
* which must be separated by other characters.
*/
void word_right(window)
windows *window;
{
int len; /* length of this line */
text_ptr p; /* next line */
copy_line(window);
if (window->ccol >= (len = linelen(g_status.line_buff))) {
/*
* at or beyond end of line, so move to start of next line
*/
un_copy_line(window);
if ((p = find_next(window->cursor)) != NULL) {
if (window->cline == window->bottom_line) {
scroll_down(window);
}
window->cursor = p;
++window->cline;
window->ccol = 0;
}
}
else {
/*
* normal word right - see comments in word_left
*/
for (;;) {
if (!myisalnum(g_status.line_buff[window->ccol])) {
break;
}
if (window->ccol == len) {
break;
}
++window->ccol;
}
if (window->ccol < len) {
for (;;) {
if (myisalnum(g_status.line_buff[window->ccol])) {
break;
}
if (window->ccol == len) {
break;
}
++window->ccol;
}
}
}
}
/*
* Name: word_delete
* Purpose: To delete from the cursor to the start of the next word.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is at the right of the line, then combine the
* current line with the next one, leaving the cursor where it
* is.
* If the cursor is on an alphanumeric character, then all
* subsequent alphanumeric characters are deleted.
* If the cursor is on a space, then all subsequent spaces
* are deleted.
* If the cursor is on a punctuation character, then all
* subsequent punctuation characters are deleted.
*/
void word_delete(window)
windows *window;
{
int len; /* length of current line */
int start; /* column that next word starts in */
text_ptr source; /* source for block move to delete word */
text_ptr dest; /* destination for block move */
long number; /* number of characters to move */
int del_len; /* number of characters to delete */
text_ptr p; /* next line in file */
int pad; /* padding spaces required */
int cr; /* does current line end with carriage return? */
int alpha; /* is the cursor char alphanumeric? */
copy_line(window);
if (window->ccol >= (len = linelen(g_status.line_buff))) {
/*
* we need to combine with the next line, if any
*/
if ((p = find_next(window->cursor)) != NULL) {
/*
* add padding if required
*/
if (g_status.line_buff[len] == '\n') {
cr = 1;
}
else {
cr = 0;
}
if (window->ccol > len) {
pad = window->ccol - len;
}
else {
pad = 0;
}
/*
* check room to combine lines
*/
if (len + pad + cr + linelen(p) >= BUFF_SIZE) {
error(WARNING, "cannot combine lines");
return;
}
/*
* do the move, fixing any marks
*/
source = g_status.line_buff + window->ccol - pad;
dest = source + pad;
number = len + pad - window->ccol + 1 + cr;
hw_move(dest, source, number);
fix_marks(window, source, (long) pad);
/*
* insert the padding
*/
while (pad--) {
*source++ = ' ';
}
/*
* remove the \n separating the two lines
*/
if (*source == '\n') {
*source = '\0';
}
/*
* let un_copy_line finish off the merge and adjust
* marks etc.
*/
un_copy_line(window);
/*
* give display a hint about how to update the screen
*/
if (window->cline < window->bottom_line) {
window_scroll_up(window->cline+1, window->bottom_line);
}
}
}
else {
/*
* normal word delete
*
* find the start of the next word
*/
start = window->ccol;
if (g_status.line_buff[start] == ' ') {
/*
* the cursor was on a space, so eat all consecutive spaces
* from the cursor onwards.
*/
for (;;) {
if (g_status.line_buff[start] != ' ') {
break;
}
++start;
}
}
else {
/*
* eat all consecutive characters in the same class (spaces
* are considered to be in the same class as the cursor
* character)
*/
alpha = myisalnum(g_status.line_buff[start++]);
for (;;) {
if (start == len) {
break;
}
if (g_status.line_buff[start] == ' ') {
/*
* the next character that is not a space will
* end the delete
*/
alpha = -1;
}
else if (alpha != myisalnum(g_status.line_buff[start])) {
if (g_status.line_buff[start] != ' ') {
break;
}
}
++start;
}
}
/*
* move text to delete word, and fix marks
*/
source = g_status.line_buff + start;
dest = g_status.line_buff + window->ccol;
number = strlen(g_status.line_buff) - start + 1;
hw_move(dest, source, number);
del_len = start - window->ccol;
fix_marks(window, dest, -(long)del_len);
}
}
/*
* Name: char_del_left
* Purpose: To delete the character to the left of the cursor.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is at the left of the line, then combine the
* current line with the previous one.
* If in unindent mode, and the cursor is on the first non-blank
* character of the line, then match the indentation of an
* earlier line.
*/
void char_del_left(window)
windows *window;
{
int len; /* length of the current line */
text_ptr source; /* source of block move to delete character */
text_ptr dest; /* destination of block move */
long number; /* number of characters to move */
text_ptr p; /* previous line in file */
int cr; /* did line end with carriage return? */
int plen; /* length of previous line */
int del_count; /* number of characters to delete */
int pos; /* the position of the first non-blank char */
copy_line(window);
len = linelen(g_status.line_buff);
if (window->ccol == 0) {
/*
* combine this line with the previous, if any
*/
if ((p = find_prev(window->cursor)) != NULL) {
if (g_status.line_buff[len] == '\n') {
cr = 1;
}
else {
cr = 0;
}
if (len + cr + (plen = linelen(p)) >= BUFF_SIZE) {
error(WARNING, "cannot combine lines");
return;
}
un_copy_line(window);
/*
* do the move and fix marks
*/
source = window->cursor;
dest = source-1;
number = g_status.end_mem - source;
hw_move(dest, source, number);
fix_marks(window, dest, -1L);
/*
* adjust the cursor line, since it is now in the middle of a
* newly formed line
*/
window->cursor = dest - prelinelen(dest);
/*
* make sure cursor stays on the screen, at the end of the
* previous line
*/
if (window->cline == window->top_line) {
scroll_up(window);
}
window_scroll_up(window->cline, window->bottom_line);
--window->cline;
if ((window->ccol = plen) >= g_display.ncols) {
window->ccol = g_display.ncols-1;
}
}
}
else {
/*
* normal delete
*
* find out how much to delete (depends on unindent mode)
*/
del_count = 1; /* the default */
if (g_status.unindent) {
/*
* Unindent only happens if the cursor is on the first
* non-blank character of the line, or if the cursor is
* beyond the end of the line.
*/
if ((pos = first_non_blank(g_status.line_buff)) == window->ccol
|| g_status.line_buff[pos] == '\n'
|| g_status.line_buff[pos] == '\0') {
/*
* now work out how much to unindent
*/
p = window->cursor;
for (;;) {
if ((p = find_prev(p)) == NULL) {
/*
* no more lines to try, so give up and just
* delete one character
*/
break;
}
if ((plen = first_non_blank((char *)p)) < window->ccol &&
*(p+plen) != '\n') {
/*
* found the line to match
*/
del_count = window->ccol - plen;
break;
}
}
}
}
/*
* move text to delete char(s), unless no chars actually there
*/
if (window->ccol - del_count < len) {
/*
* note that this may move characters beyond the end of the
* line in the line buffer, but this does not matter.
*/
source = g_status.line_buff + window->ccol;
dest = source - del_count;
number = strlen(g_status.line_buff) - window->ccol + 1;
hw_move(dest, source, number);
fix_marks(window, dest, -(long)del_count);
}
window->ccol -= del_count;
/*
* give update algorithm a hint
*/
if (del_count == 1) {
xygoto(window->ccol, window->cline);
c_delete();
}
}
}
/*
* Name: line_kill
* Purpose: To delete the line the cursor is on.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
*/
void line_kill(window)
windows *window;
{
/*
* let copy and un_copy do most of the work. Simply record that the
* current line has no characters in it!
*/
copy_line(window);
window_scroll_up(window->cline, window->bottom_line);
fix_marks(window, g_status.line_buff, -(long)strlen(g_status.line_buff));
*g_status.line_buff = '\0';
un_copy_line(window);
window->ccol = 0;
}
/*
* Name: char_del_under
* Purpose: To delete the character under the cursor.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is beyond the end of the line, then this
* command is ignored.
*/
void char_del_under(window)
windows *window;
{
text_ptr source; /* source of block move to delete character */
text_ptr dest; /* destination of block move */
long number; /* number of characters to move */
copy_line(window);
if (window->ccol >= linelen(g_status.line_buff)) {
return;
}
else {
/*
* move text to delete char, then fix marks
*/
source = g_status.line_buff + window->ccol + 1;
dest = source - 1;
number = strlen(g_status.line_buff) - window->ccol;
hw_move(dest, source, number);
fix_marks(window, dest, -1L);
/*
* give update algorithm a hint
*/
xygoto(window->ccol, window->cline);
c_delete();
}
}
/*
* Name: eol_kill
* Purpose: To delete everything from the cursor to the end of the line.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the cursor is beyond the end of the line, then this
* command is ignored.
*/
void eol_kill(window)
windows *window;
{
char *dest; /* the start of the delete area */
int len; /* the length of the current line */
copy_line(window);
if (window->ccol >= (len = linelen(g_status.line_buff))) {
return;
}
else {
/*
* truncate to delete rest of line
*/
dest = g_status.line_buff + window->ccol;
/*
* the \n at the end of the line must NOT be deleted!
*/
if (g_status.line_buff[len] == '\n') {
*dest++ = '\n';
}
len = strlen(dest);
fix_marks(window, dest, (long) (-len));
*dest = '\0';
}
}
/*
* Name: reminder
* Purpose: To remind the user that we are half-way through a two-character
* command.
* Date: October 1, 1989
* Passed: mess: the text to be displayed
* Notes: "mess" is displayed, highlighted, in the top left corner
*/
void reminder(mess)
char *mess;
{
char old_wanted; /* previous attribute */
/*
* only display message if user has not already typed the command!
*/
if (!c_avail()) {
xygoto(0, 0);
old_wanted = g_status.wanted;
set_attr(g_display.flash);
s_output(mess);
set_attr(old_wanted);
}
}
/*
* Name: goto_left
* Purpose: To move the cursor to the left of the current line.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
*/
void goto_left(window)
windows *window;
{
window->ccol = 0;
}
/*
* Name: goto_right
* Purpose: To move the cursor to the right of the current line.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
*/
void goto_right(window)
windows *window;
{
if (g_status.copied) {
window->ccol = linelen(g_status.line_buff);
}
else {
window->ccol = linelen(window->cursor);
}
/*
* keep cursor on screen
*/
if (window->ccol >= g_display.ncols) {
window->ccol = g_display.ncols-1;
}
}
/*
* Name: goto_top
* Purpose: To move the cursor to the top of the current window.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the start of the file occurs before the top of the window,
* then the start of the file is moved to the top of the window.
*/
void goto_top(window)
windows *window;
{
text_ptr cursor; /* anticipated cursor line */
un_copy_line(window);
for (; window->cline > window->top_line; window->cline--) {
if ((cursor = find_prev(window->cursor)) == NULL) {
window->cline = window->top_line;
break;
}
window->cursor = cursor;
}
}
/*
* Name: goto_bottom
* Purpose: To move the cursor to the bottom of the current window.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Notes: If the end of the file occurs before the bottom of the window,
* then the end of the file is moved to the bottom of the window.
*/
void goto_bottom(window)
windows *window;
{
text_ptr cursor;
un_copy_line(window);
for (; window->cline < window->bottom_line; window->cline++) {
if ((cursor = find_next(window->cursor)) == NULL) {
window->cline = window->bottom_line;
break;
}
window->cursor = cursor;
}
}
/*
* Name: set_tabstop
* Purpose: To set the current interval between tab stops
* Date: October 1, 1989
* Notes: Tab interval must be reasonable, and this function will
* not allow tabs more than MAX_COLS / 2.
*/
void set_tabstop()
{
char num_str[MAX_COLS]; /* tab interval as a character string */
int tab; /* new tab interval */
for (;;) {
sprintf(num_str, "%d", g_status.tab_size);
if (get_name("Tab interval: ", 1, num_str) != OK) {
return;
}
tab = atoi(num_str);
if (tab < MAX_COLS/2) {
break;
}
}
g_status.tab_size = tab;
}
/*
* Name: command
* Purpose: To input and execute a command or printable character.
* Date: October 1, 1989
* Passed: window: information allowing access to the current window
* Returns: nothing really, but needs to be compatible with other functions
* of type do_func.
* Notes: This enormous string of nested ifs is not a very modular
* way of doing things. Eventually, the user should probably
* be allowed to change the command keys without needing to
* recompile the source code. For the present, at least this
* keeps everything in the one place so it is easy to change!
*/
int command(window)
windows *window;
{
int c; /* character entered */
if (hw_printable(c = c_input())) {
/*
* if a printable character, simply insert it
*/
insert(window, c, TRUE);
}
else if (c == '\t') {
tab_key(window);
}
else if (c == '\b') {
char_del_left(window);
}
else if (c == CONTROL('G') || c == 127) {
char_del_under(window);
}
else if (c == '\r') {
insert(window, '\n', TRUE);
}
else if (c == CONTROL('N')) {
insert(window, '\n', FALSE);
}
else if (c == CONTROL('L')) {
do_last(window);
}
else if (c == CONTROL('Z')) {
scroll_down(window);
}
else if (c == CONTROL('W')) {
scroll_up(window);
}
else if (c == CONTROL('X')) {
move_down(window);
}
else if (c == CONTROL('E')) {
move_up(window);
}
else if (c == CONTROL('S')) {
move_left(window);
}
else if (c == CONTROL('D')) {
move_right(window);
}
else if (c == CONTROL('C')) {
page_down(window);
}
else if (c == CONTROL('R')) {
page_up(window);
}
else if (c == CONTROL('F')) {
word_right(window);
}
else if (c == CONTROL('A')) {
word_left(window);
}
else if (c == CONTROL('T')) {
word_delete(window);
}
else if (c == CONTROL('Y')) {
line_kill(window);
}
else if (c == CONTROL('V')) {
g_status.insert = !g_status.insert;
}
else if (c == CONTROL('J')) {
get_help(window);
}
else if (c == CONTROL('\\')) {
force_blank();
}
else if (c == CONTROL('K')) {
reminder("^K");
if ((c = c_input()) >= '0' && c <= '9') {
set_marker(window, c - '0');
}
else {
c = CONTROL(c);
if (c == CONTROL('Q')) {
quit(window);
}
else if (c == CONTROL('S')) {
save_file(window, SAVE_NORMAL);
}
else if (c == CONTROL('T')) {
save_as_file(window);
}
else if (c == CONTROL('X')) {
if (window->file_info->modified) {
save_file(window, SAVE_NORMAL);
}
/*
* The following check picks up the case where for some
* reason the file could not be written. In such a case,
* the last thing we want to do is quit the editor!
*/
if (!window->file_info->modified) {
finish(window);
}
}
else if (c == CONTROL('D')) {
save_file(window, SAVE_NORMAL);
if (!window->file_info->modified) {
finish(window);
}
}
else if (c == CONTROL('B')) {
mark_start(window);
}
else if (c == CONTROL('K')) {
mark_end(window);
}
else if (c == CONTROL('H')) {
window->file_info->visible = !window->file_info->visible;
}
else if (c == CONTROL('V')) {
block_move(window);
}
else if (c == CONTROL('Y')) {
block_delete(window);
}
else if (c == CONTROL('C')) {
block_copy(window);
}
else if (c == CONTROL('I')) {
block_indent(window);
}
else if (c == CONTROL('U')) {
block_unindent(window);
}
else if (c == CONTROL('R')) {
block_read(window, FALSE);
}
else if (c == CONTROL('@')) {
block_read(window, TRUE);
}
else if (c == CONTROL('W')) {
block_write(window);
}
else if (c == CONTROL('P')) {
block_print(window);
}
else if (c == CONTROL('F')) {
os_shell();
}
else {
error(WARNING, "unknown command: ^K^%c",
CONTROL(c)+'A'-1);
}
}
}
else if (c == CONTROL('Q')) {
reminder("^Q");
if ((c = c_input()) >= '0' && c <= '9') {
goto_marker(window, c - '0');
}
else {
c = CONTROL(c);
if (c == CONTROL('L')) {
g_status.copied = FALSE;
}
else if (c == CONTROL('Y')) {
eol_kill(window);
}
else if (c == CONTROL('F')) {
find_string(window);
}
else if (c == CONTROL('A')) {
replace_string(window);
}
else if (c == CONTROL('P')) {
goto_marker(window, PREVIOUS);
}
else if (c == CONTROL('B')) {
goto_marker(window, START_BLOCK);
}
else if (c == CONTROL('K')) {
goto_marker(window, END_BLOCK);
}
else if (c == CONTROL('S')) {
goto_left(window);
}
else if (c == CONTROL('D')) {
goto_right(window);
}
else if (c == CONTROL('E')) {
goto_top(window);
}
else if (c == CONTROL('X')) {
goto_bottom(window);
}
else if (c == CONTROL('R')) {
goto_top_file(window);
}
else if (c == CONTROL('C')) {
goto_end_file(window);
}
else if (c == CONTROL('[')) {
match_pair(window, TRUE);
}
else if (c == CONTROL(']')) {
match_pair(window, FALSE);
}
else if (c == CONTROL('I')) {
goto_line(window);
}
else {
error(WARNING, "unknown command: ^Q^%c",
CONTROL(c)+'A'-1);
}
}
}
else if (c == CONTROL('O')) {
reminder("^O");
if ((c = CONTROL(c_input())) == CONTROL('I')) {
g_status.indent = !g_status.indent;
}
else if (c == CONTROL('U')) {
g_status.unindent = !g_status.unindent;
}
else if (c == CONTROL('T')) {
set_tabstop();
}
else if (c == CONTROL('K')) {
choose_window(NULL, window);
}
else if (c == CONTROL('M')) {
size_window(window);
}
else {
error(WARNING, "unknown command: ^O^%c",
CONTROL(c)+'A'-1);
}
}
else if (c == 0) {
/*
* ignore
*/
;
}
else {
error(WARNING, "illegal (unprintable) key: %d", c);
}
xygoto(g_status.current_window->ccol, g_status.current_window->cline);
/*
* check for time to auto-save
*/
if (g_status.unsaved) {
if (time(NULL) > g_status.save_interval + g_status.save_time) {
save_file(g_status.current_window, SAVE_RECOVERY);
g_status.save_time += g_status.save_interval;
}
}
return 0; /* no meaning here */
}
/*
* Name: editor
* Purpose: To allow the full-screen editing of plain text files, in a way
* that is effective over slow serial communication lines.
* Date: October 1, 1989
* Passed: argc: number of command line arguments
* argv: text of command line arguments
* Notes: This is a separate function, rather than the main program, in
* case the hardware dependent implementation needs extra
* command line parameters etc.
*/
void editor(argc, argv)
int argc;
char *argv[];
{
char *name; /* name of file to start editing */
/*
* set up the screen
*/
initialize();
/*
* Check that user specified file to edit, if not offer help
*/
if (argc != 2) {
name = g_status.help_file;
}
else {
name = argv[1];
}
/*
* If a recovery file exists from a previous editing session,
* then perform recovery automatically.
*/
hw_copy_path(name, RECOVERY, g_status.recovery);
if (hw_fattrib(g_status.recovery) != ERROR) { /* file exists */
error(DIAG, "Recovering previous file");
if (open_window(NULL, g_status.recovery) == ERROR) {
terminate();
exit(1);
}
/*
* record no default file name when saving, and force user to save
*/
g_status.file_list->file_name[0] = '\0';
g_status.file_list->modified = TRUE;
}
else if (open_window(NULL, name) == ERROR) {
/*
* could not open window for normal edit file - out of memory?
*/
terminate();
exit(1);
}
/*
* main loop - keep updating the display and processing any commands
* forever!
*/
for (;;) {
(void) display(command, 0);
}
}